<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文件共享服务器</title>
    <script src="/static/petite-vue.iife.js" defer></script>
    <link rel="stylesheet" href="/static/pure-min.css">
    <link rel="stylesheet" href="/static/style.css">
    <script type="module">
        import { createApp } from '/static/petite-vue.es.js';
        const formatfloat = x => x.toFixed(2).replace(/\.?0*$/,'');
        function fmtsize(size) {
            if(size === undefined || size === null) return '';
            if(size < 1024)
                return size + ' B';
            size /= 1024.0;
            if(size < 1024)
                return formatfloat(size) + ' KB';
            size /= 1024.0;
            if(size < 1024)
                return formatfloat(size) + ' MB';
            size /= 1024.0;
            if(size < 1024)
                return formatfloat(size) + ' GB';
            size /= 1024.0;
            return formatfloat(size) + ' TB';
        }
        function formattime(time) {
            const date = new Date(time * 1000);
            const year = date.getFullYear();
            const month = date.getMonth() + 1;
            const day = date.getDate();
            const hour = date.getHours();
            const minute = date.getMinutes();
            const second = date.getSeconds();
            return `${year}/${month}/${day} ${hour}:${minute}:${second}`;
        }
        function formatdifftime(time) {
            time = Math.round(time);
            let millisecond = time % 1000;
            time = Math.floor(time / 1000);
            if(time < 1)
                return `${millisecond} ms`;
            let second = time % 60;
            millisecond = (millisecond + 1000 + "").substring(1);
            time = Math.floor(time / 60);
            if(time < 1)
                return `${second}.${millisecond} s`;
            let minute = time % 60;
            second = (second + 100 + "").substring(1);
            time = Math.floor(time / 60);
            if(time < 1)
                return `${minute}:${second}.${millisecond}`;
            minute = (minute + 100 + "").substring(1);
            return `${time}:${minute}:${second}.${millisecond}`;
        }
        createApp({
            items: [],
            stacks: [],
            curdir: '',
            is_upload: false,
            upload_progress: 0,
            upload_max: 100,
            upload_progress_info: '',
            async back() {
                if (this.stacks.length > 0)
                    await this.opendir(this.stacks.pop(), true);
                else
                    await this.totop();
            },
            async totop() {
                this.stacks = [];
                this.curdir = '';
                await this.opendir();
            },
            async opendir(path, b) {
                path = path ?? '';
                if(this.curdir !== '' && !b)
                    this.stacks.push(this.curdir);
                this.curdir = path;
                let resp = undefined;
                if(path === '') {
                    resp = await fetch('/listdir');
                } else {
                    const param = new URLSearchParams({root: path});
                    resp = await fetch('/listdir?'+param);
                }
                this.items = (await resp.json()).map(x => {
                    x.fmtsize = fmtsize(x.size);
                    x.fmttime = formattime(x.time);
                    return x;
                });
            },
            async open(item) {
                if(item.is_dir)
                    await this.opendir(item.path);
            },
            async upload(e) {
                if(e.target.files.length === 1) {
                    const file = e.target.files[0];
                    const form = new FormData();
                    form.append('file', file);
                    form.append('root', this.curdir);

                    await new Promise((resolve, reject) => {
                        const xhr = new XMLHttpRequest();
                        this.upload_progress_info = '';
                        const start = Date.now();

                        xhr.upload.addEventListener('progress', event => {
                            if(event.lengthComputable) {
                                this.upload_progress = event.loaded;
                                this.upload_max = event.total;
                                this.is_upload = true;

                                const percent = (event.loaded * 100 / event.total).toFixed(1);
                                const difftime = Date.now() - start;
                                const speed = event.loaded / difftime;
                                const speed_str = fmtsize(Math.round(speed * 1000)) + '/s'
                                const remain_time = (event.total - event.loaded) / speed;
                                const remain_time_str = formatdifftime(remain_time);
                                this.upload_progress_info = `${percent}% ${remain_time_str} ${speed_str}`;
                            }
                        });

                        xhr.onreadystatechange = () => {
                            if(xhr.readyState === 4) {
                                this.is_upload = false;
                                if(xhr.status === 200) {
                                    resolve(xhr.responseText);
                                } else {
                                    reject(xhr.statusText);
                                }
                            }
                        }

                        xhr.open('POST', '/upload', true);
                        xhr.send(form);
                    });
                }
                document.getElementById("upload_id").value = "";
                await this.opendir(this.curdir, true);
            },
            order(key, is_reverse) {
                const t = is_reverse ? -1 : 1;
                if(key === 'name') {
                    this.items.sort((a,b) => t * a.name.localeCompare(b.name));
                } else {
                    this.items.sort((a,b) => t * (a[key] - b[key]));
                }
            }
        }).mount();
    </script>
</head>
<body>
    <div v-scope v-cloak @vue:mounted="opendir()">
        <div v-show="is_upload">
            <progress :value="upload_progress" :max="upload_max" style="width: calc( 100% - 300px );"></progress>
            <div style="display: inline; width: 300px">{{upload_progress_info}}</div>
        </div>
        <div class="pure-g">
            <div v-show="!is_upload" class="pure-u-1-3 pure-button pure-button-primary">
                <label for="upload_id">
                    <div>上传</div>
                </label>
                <input id="upload_id" type="file" hidden @change="upload">
            </div>
            <div v-if="stacks.length !== 0 || curdir !== ''" class="pure-u-1-3 pure-button" @click="back">后退</div>
            <div v-if="stacks.length !== 0 || curdir !== ''" class="pure-u-1-3 pure-button" @click="totop">回到顶层</div>
        </div>
        <div class="pure-g">
            <div class="pure-u-1-6 pure-button" @click="order('name', false)">按名称正序</div>
            <div class="pure-u-1-6 pure-button" @click="order('name', true)">按名称倒序</div>
            <div class="pure-u-1-6 pure-button" @click="order('size', false)">按大小正序</div>
            <div class="pure-u-1-6 pure-button" @click="order('size', true)">按大小倒序</div>
            <div class="pure-u-1-6 pure-button" @click="order('time', false)">按时间正序</div>
            <div class="pure-u-1-6 pure-button" @click="order('time', true)">按时间倒序</div>
        </div>
        <table class="pure-table pure-table-bordered" style="width: 100%;">
            <thead>
                <tr>
                    <th>图标</th>
                    <th>名称</th>
                    <th>大小</th>
                    <th>修改时间</th>
                    <th>路径</th>
                    <th>下载链接</th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="item in items" :key="item.name" :class="item.is_dir ? 'dir' : ''" @click="open(item)">
                    <td><img v-if="item.path" class="pure-img" :src="'/icon?path=' + item.path" alt="" /></td>
                    <td>{{ item.name }}</td>
                    <td>{{ item.fmtsize }}</td>
                    <td>{{ item.fmttime }}</td>
                    <td>{{ item.path }}</td>
                    <td><a v-if="!item.is_dir" class="pure-button" style="font-size: 85%; background: #42b8dd;color:white" :href="'/download?path=' + item.path" :download="item.name">下载</a></td>
                </tr>
            </tbody>
        </table>
    </div>
</body>
</html>